package com.cloudlead.utils.persistence.codegen.serializer;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.mysema.codegen.CodeWriter;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.Type;
import com.mysema.query.codegen.EntityType;
import com.mysema.query.codegen.Property;
import com.mysema.query.codegen.Serializer;
import com.mysema.query.codegen.SerializerConfig;
import com.mysema.query.codegen.Supertype;
import com.mysema.util.BeanUtils;

public class SimpleModulesSerializer implements Serializer {

	private final String javadocSuffix;

	private boolean printSupertype = true;

	private Map<String, String> modules;

	/**
	 * Create a new BeanSerializer
	 */
	public SimpleModulesSerializer() {
		this(" is a Codegen businsess modules type");
	}

	/**
	 * Create a new BeanSerializer with the given javadoc suffix
	 * 
	 * @param javadocSuffix
	 */
	public SimpleModulesSerializer(String javadocSuffix) {
		this.javadocSuffix = javadocSuffix;
	}

	public void serialize(EntityType model, SerializerConfig serializerConfig,
			CodeWriter writer) throws IOException {
		String simpleName = model.getSimpleName();

		// package
		if (!model.getPackageName().isEmpty()) {
			writer.packageDecl(model.getPackageName());
		}

		Set<Supertype> superTypes = model.getSuperTypes();

		// header

		// imports
		Set<String> importedClasses = getAnnotationTypes(model);
		if (model.hasLists()) {
			importedClasses.add(List.class.getName());
		}
		if (model.hasCollections()) {
			importedClasses.add(Collection.class.getName());
		}
		if (model.hasSets()) {
			importedClasses.add(Set.class.getName());
		}
		if (model.hasMaps()) {
			importedClasses.add(Map.class.getName());
		}
		for (Supertype supertype : superTypes) {
			importedClasses.add(supertype.getType().getFullName());
		}
		writer.importClasses(importedClasses.toArray(new String[importedClasses
				.size()]));

		// javadoc
		writer.javadoc(simpleName + javadocSuffix);

		for (Annotation annotation : model.getAnnotations()) {
			writer.annotation(annotation);
		}

		if (printSupertype && superTypes != null) {
			List<Type> ifcs = new ArrayList<>();
			for (Supertype sup : superTypes) {
				ifcs.add(sup.getType());
			}
			writer.beginClass(model.getInnerType(), null,
					ifcs.toArray(new Type[ifcs.size()]));
		} else {
			writer.beginClass(model);
		}

		bodyStart(model, writer);

		addFullConstructor(model, writer);

		// fields
		for (Property property : model.getProperties()) {
			writer.privateField(property.getType(), property.getEscapedName());
		}

		// accessors
		// getter
		writer.beginPublicMethod(new ClassType(String.class), "getName");
		writer.line("return this.name();");
		writer.end();
		for (Property property : model.getProperties()) {
			String propertyName = property.getEscapedName();
			// getter
			writer.beginPublicMethod(property.getType(),
					"get" + BeanUtils.capitalize(propertyName));
			writer.line("return ", propertyName, ";");
			writer.end();
		}
		writer.beginPublicMethod(new ClassType(String.class), "getFullName");
		writer.line("return null == this.parent ? this.name() : this.parent.getFullName()"
				+ "+ \"-\" + this.getName();");
		writer.end();

		writer.beginPublicMethod(new ClassType(int.class), "getOrdinal");
		writer.line("return this.ordinal();");
		writer.end();

		bodyEnd(model, writer);

		writer.end();
	}

	protected void bodyStart(EntityType model, CodeWriter writer)
			throws IOException {
		StringBuilder seg = new StringBuilder();
		int i = 0;
		for (Entry<String, String> entry : modules.entrySet()) {
			seg.append(entry.getKey()).append("(\"").append(entry.getValue())
					.append("\", null)");
			if (i < modules.size() - 1) {
				seg.append(",");
			} else {
				seg.append(";");
			}
			i++;
		}
		writer.line(seg.toString());
		writer.line("");

	}

	protected void bodyEnd(EntityType model, CodeWriter writer)
			throws IOException {
		// template method
	}

	private Set<String> getAnnotationTypes(EntityType model) {
		Set<String> imports = new HashSet<String>();
		for (Annotation annotation : model.getAnnotations()) {
			imports.add(annotation.annotationType().getName());
		}
		return imports;
	}

	protected void addFullConstructor(EntityType model, CodeWriter writer)
			throws IOException {

		// full constructor
		String seg = "";
		int i = 0;
		for (Property property : model.getProperties()) {
			seg += property.getType().getSimpleName() + " "
					+ property.getName()
					+ ((i < model.getProperties().size() - 1) ? "," : "");
			i++;
		}
		writer.line(model.getSimpleName(), "(", seg, ") {");
		for (Property property : model.getProperties()) {
			writer.line("	this.", property.getName(), " = ",
					property.getName(), ";");
		}
		writer.line("}");
		writer.line("");
	}

	public void setPrintSupertype(boolean printSupertype) {
		this.printSupertype = printSupertype;
	}

	public void setModules(Map<String, String> modules) {
		this.modules = modules;
	}

}
